package dcutils.rpg.stats;

// Import Java JDK Classes
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

// Import DCUtils Classes
import dcutils.Range;
import dcutils.tuples.*;

/**
 * This class is designed to return stat objects based on user-defined database schemas.
 * @author dca
 */
public final class StatFactory {
   private StatFactory() {} // END constructor

   public static enum Type {Definition, Instance} // Definition or Instance Type

   //--------------------
   // create stat methods
   //--------------------

   public static Stat createStat(int cur, int max) throws Range.RangeException {
      return new Stat(cur, max);
   } // END createStat

   public static Stat createStat(int min, int cur, int max) throws Range.RangeException {
      return new Stat(min, cur, max);
   } // END createStat

   public static Stat createStat(String name, int cur, int max) throws Range.RangeException {
      return new Stat(name, cur, max);
   } // END createStat

   public static Stat createStat(String name, int min, int cur, int max) throws Range.RangeException {
      return new Stat(name, min, cur, max);
   } // END createStat

   public static Stat createStat(int id, String name, int cur, int max) throws Range.RangeException {
      return new Stat(id, name, cur, max);
   } // END createStat

   public static Stat createStat(int id, String name, int min, int cur, int max) throws Range.RangeException {
      return new Stat(id, name, min, cur, max);
   } // END createStat

   // ----------------------------
   // load Stat definition methods
   // ----------------------------

   public static Stat loadStatFromDB(
      Type type,
      Connection conn,
      String query,
      Quintet<String, String, String, String, String> fields
   ) throws SQLException, Range.RangeException {
      final List<Stat> list = loadStatsFromDB(type, conn, query, fields);

      if(1 != list.size()) {
         throw new SQLException(String.format("Expected 1 result.  Instead, received %d.", list.size()));
      } else {
         return list.get(0);
      } // END if/else
   } // END loadStatFromDB

   public static List<Stat> loadStatsFromDB(
      Type type,
      Connection conn,
      String query,
      Quintet<String, String, String, String, String> fields
   ) throws SQLException, Range.RangeException {
      final PreparedStatement stmt = conn.prepareStatement(query);
      return loadStatsFromDB(type, stmt, fields);
   } // END loadStatsFromDB

   public static List<Stat> loadStatsFromDB(
      Type type,
      PreparedStatement stmt,
      Quintet<String, String, String, String, String> fields
   ) throws SQLException, Range.RangeException {
      if(Type.Definition == type) {
         return loadStatDefinitionsFromDB(stmt, fields);
      } else if(Type.Definition == type) {
         return loadStatInstancesFromDB(stmt, fields);
      } else {
         throw new IllegalArgumentException(String.format("Type '%s' is unknown.", type.toString()));
      } // END if/else
   } // END loadStatsFromDB

   private static List<Stat> loadStatDefinitionsFromDB(
      PreparedStatement stmt,
      Quintet<String, String, String, String, String> fields
   ) throws SQLException, Range.RangeException {
      final List<Stat> list = new ArrayList<Stat>();
      final ResultSet data = stmt.executeQuery();
      final Random rand = new Random();

      while(data.next()) {
         final int id      = data.getInt(fields.first());
         final String name = data.getString(fields.second());
         final int min     = data.getInt(fields.third());
         final int max     = data.getInt(fields.fourth());
         final String def  = data.getString(fields.fifth());

         if(null != def) {
            if(def.matches("\\d+")) {
               int cur = Integer.parseInt(def);
               list.add(new Stat(id, name, min, cur, max));
            } else if(def.matches("\\d+-\\d+")) {
               int low = Integer.parseInt(def.replaceFirst("-\\d+", ""));
               int high = Integer.parseInt(def.replaceFirst("\\d+-", ""));
               int cur = low + rand.nextInt(high - low + 1);
               list.add(new Stat(id, name, min, cur, max));
            } else {
               throw new SQLException(String.format("Stat default '%s' cannot be parsed.", def));
            } // END if/else
         } else {
            list.add(new Stat(id, name, min, min, max));
         } // END if/else
      } // END data loop

      return list;
   } // END loadStatDefinitionsFromDB

   private static List<Stat> loadStatInstancesFromDB(
      PreparedStatement stmt,
      Quintet<String, String, String, String, String> fields
   ) throws SQLException, Range.RangeException {
      final List<Stat> list = new ArrayList<Stat>();
      final ResultSet data = stmt.executeQuery();

      while(data.next()) {
         final int id      = data.getInt(fields.first());
         final String name = data.getString(fields.second());
         final int min     = data.getInt(fields.third());
         final int max     = data.getInt(fields.fourth());
         final int value   = data.getInt(fields.fifth());

         list.add(new Stat(id, name, min, value, max));
      } // END data loop

      return list;
   } // END loadStatDefinitionsFromDB
} // END class StatFactory
